package nancal.mp.mvc.dev

import io.swagger.annotations.*
import nancal.mp.db.mongo.*
import nancal.mp.db.mongo.entity.*
import nancal.mp.db.mongo.entity.dev.*
import nbcp.base.annotation.*
import nbcp.base.annotation.mvc.*
import nbcp.base.comm.*
import nbcp.base.db.IdName
import nbcp.base.extend.*
import nbcp.mvc.sys.*
import nbcp.myoql.db.*
import nbcp.myoql.db.mongo.*
import nbcp.web.*

import org.springframework.data.mongodb.core.query.*
import org.springframework.web.bind.annotation.*
import java.time.*
import javax.servlet.http.*

/**
 * Created by CodeGenerator at 2023-11-05 12:25:35
 */
@Api(description = "Api模型", tags = arrayOf("DataModel"))
@RestController
@RequestMapping("/dev/data-model")
class DataModelAutoController {

    class DataModelWithParents() : DataModel() {
        var parents: List<IdName> = listOf()
        var items: List<IdName> = listOf()
    }

    class DataModelWithWbs() : DataModel() {
        var wbs: List<DataModel> = listOf()
    }

    class IdNameParent : IdName() {
        var parent: IdName? = null
    }

    @ApiOperation("列表")
    @PostMapping("/list")
    fun list(
        logicParentId: String,
        id: String, //当列表列新一条后，刷新时使用
        text: String,
        productLine: String,
        count: Array<Int>,
        contains: Array<String>,
        @org.springframework.web.bind.annotation.RequestBody query: PagerModel<Any?>,
    ): ListResult<DataModelWithParents> {

        var ret = mor.dev.dataModel.query()
            .apply {
                if (id.HasValue) {
                    this.where { it.id mongoEquals id }
                }

                if (logicParentId.HasValue) {
                    this.where { it.logicParent.id mongoEquals logicParentId }
                }

                if (productLine.HasValue) {
                    this.linkOr({ it.productLine.code mongoEquals productLine },
                        { it.productLine.name mongoEquals productLine })
                }

                if (text.HasValue) {
                    this.linkOr(
                        { it.productLine.code mongoLike text },
                        { it.productLine.name mongoLike text },
                        { it.app mongoLike text },
                        { it.name mongoLike text },
                        { it.remark mongoLike text },
                    )
                }

                if (count.size > 1) {
                    if (count[0] >= 0) {
                        this.where { it.define.items mongoSizeGreaterThan (count[0] + 1) }
                    }

                    if (count[1] >= 0) {
                        this.where { it.define.items mongoSizeLessThan (count[1] + 1) }
                    }
                }


                if (contains.contains("object")) {
                    this.where { (it.define.items.toString() + ".type") mongoEquals "Object" }
                }
                if (contains.contains("list")) {
                    this.where { (it.define.items.toString() + ".isList") mongoEquals true }
                }
            }
            .withPager(query)
            .toListResult(DataModelWithParents::class.java)


        //填充 parents
        var pids = ret.data.filter { it.logicParent.id.HasValue }
            .map { it.logicParent.id }
            .toSet();

        if (pids.isNotEmpty()) {
            var wbss = mor.dev.dataModel.aggregate()
                .beginMatch()
                .where { it.id mongoIn pids }
                .endMatch()
                .addGraphLookup({ it.logicParent.id }, { it.id })
                .toList(DataModelWithWbs::class.java);

            var wbsDb = mutableListOf<DataModel>()
            wbss.forEach {
                wbsDb.add(it);
                wbsDb.addAll(it.wbs);
            }

            ret.data.forEach {
                it.parents = fillParents(it.logicParent.id, wbsDb);
            }
        }


        var ids = ret.data.map { it.id }
        //填充 children
        var children = mor.dev.dataModel.query()
            .where { it.logicParent.id mongoIn ids }
            .select { it.id }
            .select { it.name }
            .select { it.logicParent }
            .toList(IdNameParent::class.java);

        ret.data.forEach { row ->
            row.items = children.filter { it.parent != null && it.parent!!.id == row.id }
                .map { IdName(it.id, it.name) }
        }


        return ret;
    }

    private fun fillParents(id: String, wbsDb: List<DataModel>): MutableList<IdName> {
        if (id.isNullOrEmpty()) {
            return mutableListOf();
        }

        var entity = wbsDb.firstOrNull { it.id == id }
        if (entity == null) {
            return mutableListOf()
        }

        var p = fillParents(entity.logicParent.id, wbsDb)

        p.add(IdName(entity.id, entity.name))
        return p;
    }


    class IdCnName : IdName() {
        var cnName = ""
        var treeTypes = listOf<IdName>()
    }

//    @PostMapping("/selector/{typeId}")
//    fun selector(typeId: String, search: String): List<IdCnName> {
//
//        return mor.dev.dataModel.query()
//            .where { it.treeTypes.id mongoNotEquals typeId }
//            .apply {
//
//                if (search.HasValue) {
//                    this.linkOr({ it.name mongoLike search }, { it.cnName mongoLike search })
//                } else {
//                    this.linkOr({ it.treeTypes.mongoExists(false) }, { it.treeTypes.mongoSize(0) })
//                }
//            }
//            .limit(0, 20)
//            .toList(IdCnName::class.java);
//    }


    @PostMapping("/apps")
    fun apps(): List<String> {
        var list = mor.dev.dataModel.aggregate()
            .groupWithId(
                "\$app",
                JsonMap(
                    "id" to JsonMap("\$max" to "\$_id")
                )
            )
            .toMapList()

        return list.map { it.get("id").AsString() }
    }

    @ApiOperation("详情")
    @PostMapping("/detail/{id}")
    fun detail(
        @Require id: String,
        request: HttpServletRequest
    ): ApiResult<DataModelWithParents> {

        var listResult = list("", id, "", "", arrayOf(), arrayOf(), PagerModel.of(0, 1));
        if (listResult.hasError) {
            return ApiResult.error(listResult.msg);
        }

        if (listResult.data.isEmpty()) {
            return ApiResult.error("找不到数据");
        }

        return ApiResult.of(listResult.data.first());
    }


//    @ApiOperation("拖动")
//    @PostMapping("/drag")
//    fun drag(
//        id: String,
//        pid: String,
//    ): JsonResult {
////        var dbOne = mor.dev.dataModel.queryById(id).toEntity()
////            .throwIfNull { "找不到数据模型" }
//
//        if (pid.HasValue) {
//            var parent = mor.dev.dataModel.queryById(pid).toEntity()
//                .must()
//                .elseThrow { "找不到上级模型" }
//
//            mor.dev.dataModel.updateById(id)
//                .set { it.parent to IdName(parent.id, parent.name) }
//                .exec();
//        }
//        return JsonResult();
//    }

    @ApiOperation("更新")
    @PostMapping("/save")
    fun save(
        @org.springframework.web.bind.annotation.RequestBody entity: DataModel,
        request: HttpServletRequest
    ): ApiResult<String> {
        //鉴权
        var userId = request.UserId;

        if (entity.logicParent.id.HasValue) {
            var parent = mor.dev.dataModel.queryById(entity.logicParent.id)
                .toEntity()
                .must()
                .elseThrow { "找不到上级模型" }

            entity.logicParent.name = parent.name;
        }

        if (entity.inheritParent.id.HasValue) {
            var parent = mor.dev.dataModel.queryById(entity.inheritParent.id)
                .toEntity()
                .must()
                .elseThrow { "找不到上级模型" }

            entity.inheritParent.name = parent.name;
        }


        mor.dev.dataModel.updateWithEntity(entity)
            .withColumns(request.requestParameterKeys)
            .run {
                if (entity.id.HasValue) {
                    var ret = this.execUpdate()


                    return@run ret;
                } else {
                    return@run this.execInsert()
                }
            }
            .apply {
                if (this == 0) {
                    return ApiResult.error("更新失败")
                }

                return ApiResult.of(entity.id)
            }
    }

    /**
     * 级联更新。 b.parent from a to x , then  parents: a.b.c  -->  x.b.c
     */
//    private fun syncSub(id: String, index: Int, newParents: MutableList<IdName>) {
//        BatchReader.init { skip, take ->
//            mor.dev.dataModel.query()
//                .where { MongoColumnName(it.parents.toString() + "." + index + ".id") mongoEquals id }
//                .toList()
//        }.forEach {
//            var index = it.parents.map { it.id }.indexOf(id);
//            if (index < 0) {
//                return@forEach
//            }
//
//            var list = mutableListOf<IdName>();
//            list.addAll(newParents)
//            list.addAll(it.parents.Slice(index + 1))
//
//            mor.dev.dataModel.updateById(it.id)
//                .set { it.parents to list }
//                .exec();
//        }
//    }


    data class IdApp(var id: String = "", var app: String = "");
    @PostMapping("/save-app")
    fun saveApp(@org.springframework.web.bind.annotation.RequestBody data: Array<IdApp>): JsonResult {
        data.forEach { setOne ->
            mor.dev.dataModel.updateById(setOne.id)
                .set { it.app to setOne.app }
                .exec()
        }

        return JsonResult();
    }

    data class IdProductLine(var id: String = "", var productLine: String);
    @PostMapping("/save-product-line")
    fun saveProductLine(@org.springframework.web.bind.annotation.RequestBody data: Array<IdProductLine>): JsonResult {
        var pls = data.map { it.productLine }.toSet();
        var productLines = mor.dev.productLine.query()
            .linkOr({ it.code mongoIn pls }, { it.name mongoIn pls })
            .toList();

        data.forEach { setOne ->
            var pl = productLines.firstOrNull { it.code == setOne.productLine || it.name == setOne.productLine };
            if (pl == null) {
                return@forEach
            }

            mor.dev.dataModel.updateById(setOne.id)
                .set { it.productLine to pl }
                .exec()
        }

        return JsonResult();
    }

    @PostMapping("/set-pid")
    fun setPid(id: String, pid: String) {
        if (pid.isEmpty()) {
            mor.dev.dataModel.updateById(id)
                .set { it.logicParent to IdName() }
                .exec();
            return;
        }

        var parent = mor.dev.dataModel.queryById(pid).toEntity()
            .throwIfNull { "找不到父对象" };

        mor.dev.dataModel.updateById(id)
            .set { it.logicParent to IdName(parent.id, parent.name) }
            .exec();
    }


    @ApiOperation("删除")
    @PostMapping("/delete/{id}")
    fun delete(
        @Require id: String,
        request: HttpServletRequest
    ): JsonResult {
        //鉴权
        var userId = request.UserId

        var tree = mor.dev.dataModel.query()
            .toTreeJson({ it.id }, { it.logicParent.id }, id);

        var entity = mor.dev.dataModel.queryById(id).toEntity()
        if (entity == null) {
            return JsonResult.error("找不到数据")
        }

        mor.dev.dataModel.deleteById(id)
            .exec()
            .apply {
                if (this == 0) {
                    return JsonResult.error("删除失败")
                }
                //实体上配置垃圾箱功能，可物理删除，会自动移到垃圾箱。
                return JsonResult()
            }
    }
}